/** @file    BigBrotherImpl.cpp
 *  @author  Alessandro Polo
 *  @version $Id: BigBrotherImpl.cpp 2808 2010-07-28 20:44:00Z alex $
 *  @brief
 * File containing methods for the BigBrotherImpl class.
 * The header for this class can be found in BigBrotherImpl.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include "BigBrotherImpl.h"

 #include <core/Message.h>
 #include <core/Request.h>
 #include <core/Response.h>
 #include <core/Fact.h>
 #include <core/MethodsCommon.h>
 #include <core/UserManager.h>

 #include <interfaces/services/BuildingManager.h>

//TEMP
#include <framework/building/Door.h>
 #include <framework/building/Room.h>
 #include <framework/building/Location.h>
 #include <interfaces/Device.h>
 #include <interfaces/devices/SensorMotion.h>
 #include <interfaces/devices/SensorLight.h>
 #include <interfaces/services/BuildingManager.h>
//TEMP

 #include <stdlib.h>
 #include <time.h>
 #include <algorithm>

using namespace std;
using namespace wosh;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

BigBrotherImpl::BigBrotherImpl( BundleGeneric& bundle ) : BundleGenericWorker(bundle)
 {
	this->listener = NULL;
 }

BigBrotherImpl::~BigBrotherImpl() {
	if ( isThreadRunning() ) {
		quitThread(10000);
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////// RUN THREAD

void BigBrotherImpl::runThread()
 {
	Log(LOG_VERBOSE, ":runThread(): STARTING" );

	while( this->running )
	 {
		ThreadImpl::sleepForMSec(50);
		setThreadAlive();
		if ( this->msgQueue.isEmpty() ) {
			ThreadImpl::sleepForMSec(50);
			continue;
		 }
		// get and remove it from queue
		Message* message = this->msgQueue.pop();
		if ( message == NULL ) continue;

		evalMessage(message);

		// finally delete the instance
		delete message;
	 }

	Log(LOG_VERBOSE, ":runThread(): EXITING" );
 }

////////////////////////////////////////////////////////////////////////////////////////////////// RUN THREAD
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Location* BigBrotherImpl::getLocationByURI_( const URI& uri )
 {
	std::map<string, Location*>::const_iterator it_c;
	std::map<string, Location*>::const_iterator it_c_end = this->locations.stdEnd();
	for ( it_c=this->locations.stdBegin(); it_c!=it_c_end; it_c++ ) {
		if ( it_c->second == NULL ) continue;
		if ( it_c->second->getURI() != uri ) continue;
		return it_c->second;
	 }
	return NULL;
 }

Location* BigBrotherImpl::getLocationParent_( const Location* child )
 {
	std::map<string, Location*>::const_iterator it_c;
	std::map<string, Location*>::const_iterator it_c_end = this->locations.stdEnd();
	for ( it_c=this->locations.stdBegin(); it_c!=it_c_end; it_c++ ) {
		if ( it_c->second == NULL ) continue;
		if ( it_c->second->getName() != child->getParentName() ) continue;
		return it_c->second;
	 }
	return NULL;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BigBrotherImpl::evalMessageIncoming( const Message& message )
 {
	if ( message.isEmpty() ) return;
	if ( !message.getContent()->isFact() ) return;
	msgQueue.push( message.clone() );
 }

void BigBrotherImpl::evalMessage( Message* message )
 {
	if ( message == NULL || message->isEmpty() ) return;

	if ( message->getContent()->isFact() ) {
		const Fact* fact = message->getContent()->asFact();

		this->locations.mutex().lockForWrite();
		Location* cLocation = NULL;
		Location* cLocationParent = NULL;
		std::map<string, Location*>::const_iterator it_c;
		std::map<string, Location*>::const_iterator it_c_end = this->locations.stdEnd();
		for ( it_c=this->locations.stdBegin(); it_c!=it_c_end; it_c++ ) {
			if ( it_c->second == NULL ) continue;
			if ( it_c->second->getURI() == message->getSource() ) {
				cLocation = it_c->second;
				break;
			 }
//			if ( it_c->second->isChildByUri(message->getSource().toString()) ) {
//				cLocationParent = it_c->second;
//			 }
		 }
		if ( cLocation == NULL && cLocationParent == NULL ) {
			// ..
			this->locations.mutex().unLock();
			return;
		 }

		if ( cLocation == NULL  ) {
			Log(LOG_VERBOSE, ":evalMessage(%s): Eval Fact by ChildOf %s", message->getSource().toString().c_str(), cLocationParent->getName().c_str() );

			if ( fact->getEvent() == _Device_EVENT_UserInteraction ) {
				if ( fact->hasData() && fact->getData()->isKindOf<Variant>()) {
					const Data* dataUser = dynamic_cast<const Data*>(fact->getData());
					setUserLocation_(dataUser->getData(), cLocationParent, 0.8 );
				 }
				//else
				//	recognizeUser(cLocation, message);
			 }
			else if ( fact->getEvent() == _CommunicatorService_EVENT_UserOnline ) {
				if ( fact->hasData() && fact->getData()->isKindOf<Variant>()) {
					const Data* dataUser = dynamic_cast<const Data*>(fact->getData());
					setUserLocation_(dataUser->getData(), cLocationParent, 0.5 );
				 }
				else
					recognizeUser_(true, cLocation, message);
			 }
			else if ( fact->getEvent() == _CommunicatorService_EVENT_UserOffline ) {
				if ( fact->hasData() && fact->getData()->isKindOf<Variant>()) {
					const Data* dataUser = dynamic_cast<const Data*>(fact->getData());
					setUserLocation_(dataUser->getData(), cLocationParent, -0.5 );
				 }
				else
					recognizeUser_(false, cLocation, message);
			 }

			this->locations.mutex().unLock();
			return;
		 }
		else { // then source is an Location
			Log(LOG_VERBOSE, ":evalMessage(%s): Eval Fact", cLocation->getName().c_str() );

			if ( fact->getEvent() == _Room_EVENT_PresenceChanged ) {
				// room presence has been detected
				if ( fact->hasData() && fact->getData()->isKindOf<Variant>()) {
					const Data* dataUser = dynamic_cast<const Data*>(fact->getData());
					if ( dataUser->getData() == "DETECTED" )
						recognizeUser_(true, cLocation, message);
					else if ( dataUser->getData() == "EMPTY" )
						recognizeUser_(false, cLocation, message);
				 }
			 }
			else if ( fact->getEvent() == _Device_EVENT_StateChanged ) { // must be a motion sensor
				// room presence has been detected
				if ( fact->hasData() && fact->getData()->isKindOf<Variant>()) {
					const Data* dataUser = dynamic_cast<const Data*>(fact->getData());
					if ( dataUser->getData() == "100" )
						recognizeUser_(true, cLocation, message);
					else if ( dataUser->getData() == "0" )
						recognizeUser_(false, cLocation, message);
				 }
			 }
		 }
		this->locations.mutex().unLock();
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BigBrotherImpl::setUserLocation_( const std::string& username, Location* hLoc, double probability )
 {
	if ( hLoc == NULL ) return;
	// probability = 0.0 means that user IS NOT there for sure
	// probability = 1.0 means that user IS there for sure

	// update current location
	hLoc->getUsers()[username].timestamp = Utilities::std_time();
	hLoc->getUsers()[username].probability = probability;

	Log(LOG_INFO, ":setUserLocation_(%s) : in %s [%f]", username.c_str(), hLoc->getName().c_str(), probability );

	// update all other locations
	std::map<string, Location*>::const_iterator it_c;
	std::map<string, Location*>::const_iterator it_c_end = this->locations.stdEnd();
	for ( it_c=this->locations.stdBegin(); it_c!=it_c_end; it_c++ ) {
		if ( it_c->second == NULL || it_c->second == hLoc ) continue;
		if ( !it_c->second->isUserInLocation(username) ) continue;
		it_c->second->getUsers()[username].timestamp = Utilities::std_time();
		it_c->second->getUsers()[username].probability = 0.0;
	 }

	WRESULT ret = UserManager::setUserLocation( username, hLoc->getName(), probability, 1 );
	if ( WFAILED(ret) )
		Log(LOG_WARNING, ":setUserLocation_(%s) in %s @ %f", username.c_str(), hLoc->getName().c_str(), probability );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BigBrotherImpl::setUserLocation( const std::string& username, const string& new_location, double probability )
 {
	MutexLockerWrite mL(&this->locations.mutex());
	Location* hLoc =this->locations.key_( new_location );
	if ( hLoc == NULL ) return;
	setUserLocation_(username, hLoc, probability);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
